/*
    Project:        Home Power Control
    File:           HPCMCP3008.cpp
    Author:         christian@karunamusic.at
    Date:           02.2015
    Description:    Class to access and query the A/D converter-chip MCP3008 via SPI.
 */

#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdexcept>
#include <linux/spi/spidev.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include "Logger/HPCLogger.h"
#include "HPCMCP3008.h"

using namespace std;

HPCMCP3008::HPCMCP3008()
    : QObject(), m_Mode(SPI_MODE_0), m_BitsPerWord(8), m_Speed(1000000), m_spifd(-1)
{
    spiOpen("/dev/spidev0.0");
}

HPCMCP3008::HPCMCP3008( const QString& devspi, unsigned char spiMode, unsigned int spiSpeed, unsigned char spiBitsPerWord)
    : QObject(), m_Mode(spiMode), m_BitsPerWord(spiBitsPerWord), m_Speed(spiSpeed), m_spifd(-1)
{
    spiOpen(devspi);
}

HPCMCP3008::~HPCMCP3008()
{
    spiClose();
}

int HPCMCP3008::spiOpen( const QString& devspi )
{
    int ret = -1;

    m_spifd = open( devspi.toStdString().c_str(),O_RDWR );
    if ( m_spifd < 0 )
    {
        THROW( "could not open SPI device " << devspi );
    }

    ret = ioctl( m_spifd,SPI_IOC_WR_MODE,&(m_Mode) );
    if ( ret < 0 )
    {
        THROW( "could not set SPIMode (WR)...ioctl failed" );
    }

    ret = ioctl( m_spifd,SPI_IOC_RD_MODE,&(m_Mode) );
    if ( ret < 0 )
    {
        THROW( "could not set SPIMode (RD)...ioctl failed" );
    }

    ret = ioctl( m_spifd,SPI_IOC_WR_BITS_PER_WORD,&(m_BitsPerWord) );
    if ( ret < 0 )
    {
        THROW( "could not set SPI BitsPerWord (WR)...ioctl failed" );
    }

    ret = ioctl( m_spifd,SPI_IOC_RD_BITS_PER_WORD,&(m_BitsPerWord) );
    if ( ret < 0 )
    {
        THROW( "could not set SPI BitsPerWord (RD)...ioctl failed" );
    }

    ret = ioctl( m_spifd,SPI_IOC_WR_MAX_SPEED_HZ,&(m_Speed) );
    if ( ret < 0 )
    {
        THROW( "could not set SPI Speed (WR)...ioctl failed" );
    }

    ret = ioctl( m_spifd,SPI_IOC_RD_MAX_SPEED_HZ,&(m_Speed) );
    if ( ret < 0 )
    {
        THROW( "could not set SPI Speed (RD)...ioctl failed" );
    }

    return ret;
}

int HPCMCP3008::spiClose()
{
    int ret = -1;

    ret = close( m_spifd );
    if(ret < 0)
    {
        THROW( "could not close SPI device" );
    }
    return ret;
}

int HPCMCP3008::spiWriteRead( unsigned char *data, int length )
{
    struct spi_ioc_transfer spi[length];
    int i = 0;
    int ret = -1;

    for ( i = 0; i < length; i++ )
    {
        spi[i].tx_buf = (unsigned long)(data + i);
        spi[i].rx_buf = (unsigned long)(data + i);
        spi[i].len = sizeof(*(data + i));

        spi[i].delay_usecs = 0;
        spi[i].speed_hz = m_Speed;
        spi[i].bits_per_word = m_BitsPerWord;
        spi[i].cs_change = 0;
    }

    ret = ioctl( m_spifd,SPI_IOC_MESSAGE(length),&spi );
    if ( ret < 0 )
    {
        THROW( "problem transmitting spi data..ioctl failed" );
    }

    return ret;
}
